#include "../include/EventLoop.h"
#include "../include/Acceptor.h"
#include "../include/TcpConnection.h"
#include "../include/MutexLockGuard.h"
#include "../include/Logger.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/eventfd.h>

namespace myNameSpace {
EventLoop::EventLoop(Acceptor &acceptor): _acceptor(acceptor), _epollFd(createEpollFd()), _eventFd(createEventFd()), _eventVector(1024), _tcpConnections(), _mutex(), _sendingTasks(), _isLooping(false) {
    addEpollReadEvent(_acceptor.getFd());
    addEpollReadEvent(_eventFd);
}

EventLoop::~EventLoop() {
    if (_epollFd > 0) {
        close(_epollFd);
    }
    if (_eventFd > 0) {
        close(_eventFd);
    }
}

void EventLoop::loop() {
    _isLooping = true;
    while (_isLooping) {
        waitEpollFd();
    }
}

void EventLoop::unloop() {
    _isLooping = false;
}

void EventLoop::setNewConnectionCallback(TcpConnectionCallback &&cb) {
    _onNewConnection = std::move(cb); 
}

void EventLoop::setMessageCallback(TcpConnectionCallback &&cb) {
    _onMessage = std::move(cb);
}

void EventLoop::setCloseCallback(TcpConnectionCallback &&cb) {
    _onClose = std::move(cb);
}

void EventLoop::addSendTaskAndNotify(TaskCallback &&cb) {
    {
        MutexLockGuard lockGuard(_mutex);
        _sendingTasks.emplace_back(std::move(cb));
    }
    moqi::LOG_INFO("Thread %u: Pushback response task.", pthread_self());
    setEventFlag();
}

int EventLoop::createEpollFd() {
    int epollFd = epoll_create1(0);
    if (epollFd == -1) {
        perror("epoll_create1");
    }
    return epollFd;
}

void EventLoop::addEpollReadEvent(int fd) {
    struct epoll_event event;
    memset(&event, 0, sizeof(event));
    event.data.fd = fd;
    event.events = EPOLLIN;
    int ret = epoll_ctl(_epollFd, EPOLL_CTL_ADD, fd, &event);
    if (ret == -1) {
        perror("epoll_ctl");
    }
}

void EventLoop::delEpollReadEvent(int fd) {
    struct epoll_event event;
    memset(&event, 0, sizeof(event));
    event.data.fd = fd;
    int ret = epoll_ctl(_epollFd, EPOLL_CTL_DEL, fd, &event);
    if (ret == -1) {
        perror("epoll_ctl");
    }
}

void EventLoop::waitEpollFd() {
    int readyCount = epoll_wait(_epollFd, _eventVector.data(), _eventVector.size(), -1);
    if (readyCount == -1 && errno == EINTR) {
        return;
    }
    if (readyCount == -1) {
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < readyCount; ++i) {
        if (_eventVector[i].data.fd == _acceptor.getFd()) {
            handleNewConnection();
        }
        else if (_eventVector[i].data.fd == _eventFd) {
            clearEventFlag();
            doSendTask();
        }
        else {
            handleMessage(_eventVector[i].data.fd);
        }
    }
}

void EventLoop::handleNewConnection() {
    int netFd = _acceptor.accept();
    if (netFd == -1) {
        perror("accept");
        return;
    }
    addEpollReadEvent(netFd);
    shared_ptr<TcpConnection> pTcpConnection(new TcpConnection(netFd, this));
    pTcpConnection->setNewConnectionCallback(_onNewConnection);
    pTcpConnection->setMessageCallback(_onMessage);
    pTcpConnection->setCloseCallback(_onClose);
    _tcpConnections[netFd] = pTcpConnection;
    pTcpConnection->handleNewConnectionCallback();
}

void EventLoop::handleMessage(int netFd) {
    if (_tcpConnections.count(netFd) > 0) {
        shared_ptr<TcpConnection> pTcpConnection = _tcpConnections[netFd];
        if (pTcpConnection->isClosed()) {
            pTcpConnection->handleCloseCallback();
            delEpollReadEvent(netFd);
            _tcpConnections.erase(netFd);
        }
        else {
            pTcpConnection->handleMessageCallback();
        }
    }
}

int EventLoop::createEventFd() {
    int eventFd = eventfd(0, 0);
    if (eventFd == -1) {
        perror("eventfd");
    }
    return eventFd;
}

void EventLoop::setEventFlag() {
    uint64_t one = 1;
    int ret = write(_eventFd, &one, sizeof(one));
    if (ret != sizeof(one)) {
        perror("write");
    }
    moqi::LOG_INFO("Thread %u: Write eventfd.", pthread_self());
}

void EventLoop::clearEventFlag() {
    uint64_t count = 0;
    int ret = read(_eventFd, &count, sizeof(count));
    moqi::LOG_INFO("Read count from eventFd: %u.", count);
    if (ret != sizeof(count)) {
        perror("read");
    }
    moqi::LOG_INFO("Thread %u: Read eventfd.", pthread_self());
}

void EventLoop::doSendTask() {
    vector<TaskCallback> tmp;
    {
        MutexLockGuard lockGuard(_mutex);
        tmp.swap(_sendingTasks);
    }
    for (auto &taskCallback : tmp) {
        taskCallback();
    }
}
} // end of namespace myNameSpace
